package com.senyint.hip.fhir.provider;

import java.util.ArrayList;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.stereotype.Component;
 
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.Read;
 
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;

/**
 * *******description*******
 * 检验报告
 * *************************
 *
 * @author ChenPan
 * @date 2018/10/29 10:58
 */
@Component
public class DiagnosticReportProvider implements IResourceProvider {

    /**
     * 临时存储数据
     */
    private Map<Long, Deque<DiagnosticReport>> reportsRes = new HashMap<Long, Deque<DiagnosticReport>>();
    private long reportNextId = 1;

    @Override
    public Class<? extends IBaseResource> getResourceType() {
        return DiagnosticReport.class;
    }

    @Create()
    public MethodOutcome createDiagnosticReport(@ResourceParam DiagnosticReport report) {

        long id = reportNextId++;

        String versionId = addNewVersionReport(report, id);

        MethodOutcome retVal = new MethodOutcome();
        retVal.setId(new IdType("DiagnosticReport", String.valueOf(id), versionId));

        // You can also add an OperationOutcome resource to return
        // This part is optional though:
        OperationOutcome outcome = new OperationOutcome();
        outcome.setIdElement(new IdType("DiagnosticReport", String.valueOf(id), versionId));
        outcome.addIssue().setDiagnostics("新增检验报告成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }

    private String addNewVersionReport(DiagnosticReport report, long id) {

        Date publishedDate;

        if (!reportsRes.containsKey(id)) {
            reportsRes.put(id, new LinkedList<DiagnosticReport>());
            publishedDate = InstantDt.withCurrentTime().getValue();
        } else {
            DiagnosticReport oldReport = reportsRes.get(id).getLast();
            publishedDate = oldReport.getMeta().getLastUpdated();

        }


        report.getMeta().setLastUpdated(InstantDt.withCurrentTime().getValue());
        Deque<DiagnosticReport> existingVersions = reportsRes.get(id);

        String newVersion = Integer.toString(existingVersions.size());

        //版本号
        report.getMeta().setVersionId(newVersion);

        // Create an ID with the new version and assign it back to the resource
        IdDt newId = new IdDt("DiagnosticReport", Long.toString(id), newVersion);
        report.setId(newId);

        existingVersions.add(report);
        return newVersion;
    }


    @Search()
    public List<DiagnosticReport> findByLabCode(@OptionalParam(name = "itemName") String itemName,@OptionalParam(name = "_id") String id) {

        LinkedList<DiagnosticReport> retVal = new LinkedList<DiagnosticReport>();

        if(StringUtils.isEmpty(itemName)&& StringUtils.isEmpty(id)) {
        	return findDrUsingArbitraryCtriteria();
        }
        
        for (Deque<DiagnosticReport> nextReportList : reportsRes.values()) {
            DiagnosticReport nextReport = nextReportList.getLast();
            Long idPartAsLong = nextReport.getIdElement().getIdPartAsLong();
            for(Resource resource:nextReport.getContained()) {
            	Observation ob = (Observation) resource;
            	CodeableConcept codeableConcept = ob.getCode();
            	String code = codeableConcept.getCoding().get(0).getCode();
            	
            	String name = codeableConcept.getCoding().get(0).getDisplay();
            	if(StringUtils.isNotEmpty(itemName) && StringUtils.isNotEmpty(id)) {
            		if (itemName.equals(name)&&id.equals(idPartAsLong.toString())) {
                        retVal.add(nextReport);
                      break;
                    }
            	}else if(StringUtils.isNotEmpty(itemName) && StringUtils.isEmpty(id)) {
            		if (itemName.equals(name)) {
                        retVal.add(nextReport);
                      break;
                    }
            	}else if(StringUtils.isEmpty(itemName) && StringUtils.isNotEmpty(id)) {
            		if (id.equals(idPartAsLong.toString())) {
                        retVal.add(nextReport);
                      break;
                    }
            	}
            	  
            	
            	
            }
            
            
         
       /*     CODELOOP:
            for (Coding nextCoding : nextReport.getCode().getCoding()) {
                //nextCoding.getCode() 项目编码  nextCoding.getDisplay() 项目名称
            	//theLabCode.equals(nextCoding.getCode()) || itemName.equals(nextCoding.getDisplay())
                if (itemName.equals(nextCoding.getDisplay())) {
                   // retVal.add(nextReport);
                    break CODELOOP;
                }
            }*/

        }

        return retVal;
    }

    @Search
    public List<DiagnosticReport> findDrUsingArbitraryCtriteria() {
        LinkedList<DiagnosticReport> retVal = new LinkedList<DiagnosticReport>();

        for (Deque<DiagnosticReport> nextdrList : reportsRes.values()) {
            DiagnosticReport nextDr = nextdrList.getLast();
            retVal.add(nextDr);
        }

        return retVal;
    }

    @Read(version = true)
    public DiagnosticReport readDiagnosticReport(@IdParam IdType theId) {
        Deque<DiagnosticReport> retVal;
        try {
            retVal = reportsRes.get(theId.getIdPartAsLong());
        } catch (NumberFormatException e) {
            /*
             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
             */
            throw new ResourceNotFoundException(theId);
        }

        if (theId.hasVersionIdPart() == false) {
            return retVal.getLast();
        } else {
            for (DiagnosticReport nextVersion : retVal) {
                String nextVersionId = nextVersion.getIdElement().getVersionIdPart();
                if (theId.getVersionIdPart().equals(nextVersionId)) {
                    return nextVersion;
                }
            }
            // No matching version
            throw new ResourceNotFoundException("Unknown version: " + theId.getValue());
        }

    }

    @History()
    public List<DiagnosticReport> readDiagnosticReportBundle(@IdParam IdType theId) {
        Deque<DiagnosticReport> retVal;
        try {
            retVal = reportsRes.get(theId.getIdPartAsLong());
        } catch (NumberFormatException e) {
            /*
             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
             */
            throw new ResourceNotFoundException(theId);
        }

        List<DiagnosticReport> reportBundle = new ArrayList<>();
        for (DiagnosticReport nextReport : retVal) {
            reportBundle.add(nextReport);
        }

        return reportBundle;
    }

    @Update()
    public MethodOutcome updateDiagnosticReport(@IdParam IdType theId, @ResourceParam DiagnosticReport theReport) {

        Long id;
        try {
            id = theId.getIdPartAsLong();
        } catch (DataFormatException e) {
            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
        }

        /*
         * Throw an exception (HTTP 404) if the ID is not known
         */
        if (!reportsRes.containsKey(id)) {
            throw new ResourceNotFoundException(theId);
        }

        String versionId = addNewVersionReport(theReport, id);

        MethodOutcome retVal = new MethodOutcome();
        retVal.setId(new IdType("DiagnosticReport", String.valueOf(id), versionId));

        // You can also add an OperationOutcome resource to return
        // This part is optional though:
        OperationOutcome outcome = new OperationOutcome();
        outcome.setIdElement(new IdType("DiagnosticReport", String.valueOf(id), versionId));
        outcome.addIssue().setDiagnostics("更新检验报告成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }


    @Delete()
    public MethodOutcome removeDiagnosticReport(@IdParam IdType theId) {
        Long id;
        try {
            id = theId.getIdPartAsLong();
        } catch (DataFormatException e) {
            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
        }

        if (!reportsRes.containsKey(id)) {
            throw new ResourceNotFoundException(theId);
        }

        reportsRes.remove(id);

        MethodOutcome retVal = new MethodOutcome();
        OperationOutcome outcome = new OperationOutcome();
        outcome.addIssue().setDiagnostics("删除成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }
}
